package it.unica.informatica.enotes.manager;

import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import it.unica.informatica.enotes.NotesActivity;
import it.unica.informatica.enotes.notes.AttachmentItem;
import it.unica.informatica.enotes.notes.Note;

/**
 * This class perform all the operation related to the translation of a note to and from a file to send via mail
 *
 * @author Mirko Marras
 * @version 1.0
 */
public class NoteSenderManager {

   /**
    * The file extension used to save the file that the app send by email
    */
   private static final String NOTE_EXT = ".enotes";

   /**
    * The file extension used to save the file that contains the json of the note
    */
   private static final String JSON_EXT = ".json";

     /* ********************************************************************************************
     *                 METHODS TO CREATE THE FILE WITH THE JSON OF THE NOTE                        *
     **********************************************************************************************/

   /**
    * Create a temporary file that stores the content of the note as JSON
    *
    * @param note The note to send
    */
   public static void createJSONFile(Note note) {
      /* Create the name of the file based on the note title */
      String filename = note.getTitle().replace(' ', '_');

      /* Create the associated file */
      File file = new File(AttachmentsStoringManager.baseCacheDir + filename + JSON_EXT);
      FileWriter fileWriter = null;

      /* Write the JSON code of the note to the file */
      try {
         fileWriter = new FileWriter(file);
         fileWriter.write(JsonManager.toJSON(note));
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
         try {
            if (fileWriter != null) {
               fileWriter.flush();
               fileWriter.close();
            }
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }

   /**
    * Create a note object based on the content of the file at the file path
    *
    * @param filePath The file path of the file that contains the content of the note as JSON
    * @return The note object that contains the data associated to the JSON string into the file
    */
   public static Note readJSONFile(String filePath) {
      /* This Note will store the note associated to the the file with the file path parameter */
      Note note = new Note();
      File file = new File(filePath);

      /* Create a JSONObject that store the JSON string contained in the file */
      JSONObject json = null;
      JSONParser parser = new JSONParser();
      try {
         FileReader fileReader = new FileReader(file);
         json = (JSONObject) parser.parse(fileReader);
      } catch (Exception ex) {
         ex.printStackTrace();
      }

      /* Set the properties of the note to the retrieved values */
      note.setTitle(file.getName().replace(JSON_EXT, "").replace('_', ' '));
      if (json != null) {
         note.setContent(JsonManager.getNoteContent(json.toJSONString()));
         note.setAttachments(JsonManager.getNoteAttachments(json.toJSONString()));
      }

      /* Delete JSON file */
      file.delete();

      return note;
   }

   /**
    * Get a absolute path of the note file to import inside the application
    *
    * @param activity The activity that imports the file
    * @return The absolute path of the file to import
    */
   public static String getPathToImport(NotesActivity activity) {
      Intent intent = activity.getIntent();
      InputStream is = null;
      FileOutputStream os = null;
      String fullPath = null;

      /* Retrieve the name of the file to import */
      try {
         Uri uri = intent.getData();
         String scheme = uri.getScheme();
         String name = null;

         if (scheme.equals("file")) {
            List<String> pathSegments = uri.getPathSegments();

            if (pathSegments.size() > 0)
               name = pathSegments.get(pathSegments.size() - 1);
         } else if (scheme.equals("content")) {
            Cursor cursor = activity.getContentResolver().query(
                    uri,
                    new String[]{MediaStore.MediaColumns.DISPLAY_NAME},
                    null,
                    null,
                    null);

            cursor.moveToFirst();

            int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
            if (nameIndex >= 0)
               name = cursor.getString(nameIndex);
         } else {
            return "";
         }

         if (name == null) {
            return "";
         }

         int n = name.lastIndexOf(".");
         String fileName, fileExt;

         if (n == -1) {
            return "";
         } else {
            fileName = name.substring(0, n);
            fileExt = name.substring(n);
         }

         /* Temporary copy of the file inside the cache of the application */
         fullPath = AttachmentsStoringManager.baseExternalDir + fileName + fileExt;

         is = activity.getContentResolver().openInputStream(uri);
         os = new FileOutputStream(fullPath);

         byte[] buffer = new byte[4096];
         int count;

         while ((count = is.read(buffer)) > 0)
            os.write(buffer, 0, count);

         os.close();
         is.close();
      } catch (Exception e) {
         if (is != null) {
            try {
               is.close();
            } catch (Exception e1) {
               e1.printStackTrace();
            }
         }

         if (os != null) {
            try {
               os.close();
            } catch (Exception e1) {

            }
         }

         if (fullPath != null) {
            File f = new File(fullPath);
            f.delete();
         }

      }
      return fullPath;
   }

     /* ********************************************************************************************
     *                       METHODS TO ZIP AND UNZIP A NOTE SEND BY EMAIL                        *
     **********************************************************************************************/

   /**
    * Create a zip file that contains a file with the note in JSON format and all attachment files
    *
    * @param note The note to send
    * @return The zip file that contains a file with the note in JSON format and all attachment files
    */
   public static File setFileForMail(Note note) {
      final int BUFFER = 8192;
      final String dirName = AttachmentsStoringManager.FOLDER_PREFIX + note.getId();
      String filename = note.getTitle().replace(' ', '_');

      /* Delete the content of the cache file */
      AttachmentsStoringManager.deleteCache();

      /* Create the new file associated to the mail attachment */
      File file = new File(AttachmentsStoringManager.baseCacheDir + File.separator + filename + NOTE_EXT);
      if (file.exists()) file.delete();

      try {
         FileOutputStream dest = new FileOutputStream(AttachmentsStoringManager.baseCacheDir + File.separator + filename + NOTE_EXT);
         ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));

         byte data[] = new byte[BUFFER];

         /* Add a copy of each attachment file to the email file */
         for (AttachmentItem item : note.getAttachments()) {
            String path = AttachmentsStoringManager.baseExternalDir + File.separator + dirName + File.separator + item.getName();
            FileInputStream fi = new FileInputStream(path);
            BufferedInputStream origin = new BufferedInputStream(fi, BUFFER);

            ZipEntry entry = new ZipEntry(path.substring(path.lastIndexOf("/") + 1));
            out.putNextEntry(entry);

            int count;
            while ((count = origin.read(data, 0, BUFFER)) != -1) {
               out.write(data, 0, count);
            }

            origin.close();
         }

         /* Create the file associated to the note in JSON format and insert it to the zip */
         NoteSenderManager.createJSONFile(note);

         String path = AttachmentsStoringManager.baseCacheDir + filename + JSON_EXT;
         FileInputStream fi = new FileInputStream(path);
         BufferedInputStream origin = new BufferedInputStream(fi, BUFFER);

         ZipEntry entry = new ZipEntry(path.substring(path.lastIndexOf("/") + 1));
         out.putNextEntry(entry);

         int count;
         while ((count = origin.read(data, 0, BUFFER)) != -1) {
            out.write(data, 0, count);
         }

         origin.close();

         out.close();
      } catch (Exception e) {
         e.printStackTrace();
      }

      return new File(AttachmentsStoringManager.baseCacheDir + File.separator + filename + NOTE_EXT);
   }

   /**
    * Get a note object created from the information stored in the zip file received from email
    *
    * @param filepath The filepath of the file received by email
    * @return The note object that contains all the information stored for the note in the zip file received
    */
   public static Note getNoteFromMail(String filepath) {
      final int BUFFER = 2048;
      Note note = new Note();

      /* The standard name of the directory for the attachments of a note is ID<id> es. ID8 */
      final String dirName = AttachmentsStoringManager.FOLDER_PREFIX + AttachmentsStoringManager.IMPORT_PREFIX;

      /* If not exists, create the directory for the note */
      File noteDirectory = new File(AttachmentsStoringManager.baseExternalDir, dirName);
      AttachmentsStoringManager.deleteDirAppMemory(AttachmentsStoringManager.IMPORT_PREFIX);
      noteDirectory.mkdirs();

      try {
         FileInputStream fin = new FileInputStream(filepath);
         ZipInputStream zin = new ZipInputStream(fin);
         ZipEntry ze;

         while ((ze = zin.getNextEntry()) != null) {
            byte[] data = new byte[BUFFER];

            String ext = ze.getName().substring(ze.getName().lastIndexOf("."));

            /* If it is the file .notes that contains the note in the JSON format */
            if (ext.equals(JSON_EXT)) {
               FileOutputStream fout = new FileOutputStream(AttachmentsStoringManager.baseExternalDir + File.separator + ze.getName());
               int count;
               while ((count = zin.read(data, 0, BUFFER)) != -1) {
                  fout.write(data, 0, count);
               }

               zin.closeEntry();
               fout.close();

               /* Set the new note object wih the information of the json file */
               note = NoteSenderManager.readJSONFile(AttachmentsStoringManager.baseExternalDir + File.separator + ze.getName());
            } else { /* If it is an attachment file to copy in the appropriate folder for the note to import */
               FileOutputStream fout = new FileOutputStream(AttachmentsStoringManager.baseExternalDir + File.separator + dirName +
                       File.separator + ze.getName());
               int count;
               while ((count = zin.read(data, 0, BUFFER)) != -1) {
                  fout.write(data, 0, count);
               }
               zin.closeEntry();
               fout.close();
            }
         }

         zin.close();

      } catch (Exception e) {
         e.printStackTrace();
      }

      /* Delete imported file */
      File file = new File(filepath);
      if(file.exists()) {
         file.delete();
      }

      return note;
   }
   
}
